الأحد , 05 مارس,2006
الموافق : 5 صفر,1427
  
 

 

 

 


إعلانك في الموسوعة يحقق طموحاتك



1 عضو قيّم هذا الدرس:

تقييم الدرس: 5 من 5


تحميل واستدعاء دوال وإجراءات DLL

(بواسطة : نجاة | بتاريخ : 17 أبريل 2005 )
اخبر صديق طباعة

 

بسم الله الرحمن الرحيم

تحميل واستدعاء دوال وإجراءات DLL

مقدمة

بعيدا عن تعقيد الدرس السابق الذى يكفيك منه بشكل مبدئى أنك عرفت الشكل العام لوحدة DLL وعرفت كيف تنجزها ،لنبدأ فى هذا الدرس معرفة النصف الثاني من القصة التى شرحنا فى الدرس السابق نصفها الأول وهو إنجاز وحدة DLL ، هذا النصف الأخير هو كيفية استدعاء أو استيراد أوتحميل دوال وإجراءات DLL .

 

تحميل DLLs إلى تطبيقك :

قبل أن تتمكن من استخدام دالة أو إجراء موجود فى DLL فإنك بحاجة إلى تحميل DLL إلى الذاكرة ، تحميل    DLL  فى RunTime يمكن أن يكون بإحدى طريقتين :

1-التحميل الأستاتيكي Static Loading .

2-التحميل الديناميكي Dynamic Loading .

لكل من الطريقتين حسناتها وعيوبها ،لذا سأوضح الفرق بينهما :

1-التحميل الأستاتيكي:

يقصد به أن ال DLL سيُحمّل أوتوماتيكياً عندما يُنفّذ التطبيق الذي قام باستدعاء DLL ،ولاستخدام هذا النوع من الإستدعاء عليك أن تصرح عن الدالة أو الإجراء الموجود فى DLL باستخدام الكلمة المحجوزة External .

وطالما أن DLL قد حمل عند لحظة تحميل التطبيق بشكل أوتوماتيكي فكل ما عليك فعله لاستدعاء دالة أو إجراء موجود فى DLL هو استدعاء هذه الدالة أو الإجراء بشكل اعتيادي كما تستدعي أي دالة أو إجراء فى Object Pascal .

 حسنة هذا النوع من التحميل هى أنه  الأسهل للوصول إلى دوال وإجراءات DLL ،وعيبه أن DLL يحمل فى لحظة تحميل البرنامج وبالتالى إذا احتوى DLL على عيوب ما فإن البرنامج سيواجه مشاكل فى تحميله.

2-التحميل الديناميكي:

يقصد به أنك تستطيع تحميل DLL عندما تحتاج إليه وتوقف تحميله عندما لا تتحتاج للتعامل معه كل ذلك أثناء حياة تطبيقك (وجوده بالذاكرة) .

لهذا النوع من التحميل ميزاته وعيبه ،الميزة الأولى هى أنك تستخدم الذاكرة عندما تحتاج إليها فقط ، أي أنك تستغل الذاكرة بفعالية أكبر ، الميزة الأخرى هى أن برنامجك سيحمل بشكل أسرع لأنه عند لحظة تحميل تطبيقك لن يحمل كل الكود الذى تحتاجه لتنفيذه (المقصود جزء DLL).

العيب الأساسي لاستخدام التحميل الديناميكي أنه يحتاج إلى عمل أكثر من جانبك.

أ-لتحميل DLL ديناميكيا عليك استخدام دالة Windows API التى تدعى Loadlibrary .

ب-عند انتهاء عملك مع DLL استخدم الدالة FreeLibrary لإلغاء تحميله من الذاكرة .

ج-استخدم الدالة GetprocAddress للحصول على مؤشر للدالة أو الإجراء الذى تحتاج لاستدعائه.

 

استدعاء الدوال والإجراءات الموجودة في DLLs :

كما قلنا سابقاً فإن الطريقة التى تستخدمها لاستدعاء دالة أو إجراء فى DLL تعتمد على ما إذا كان DLL قد حمّل أستاتيكياً أم ديناميكياً .

أولاً الإستدعاء باستعمال التحميل الأستاتيكي :

لاستدعاء دالة أو إجراء ال DLL  الذي حمل ستاتيكياً عملية بسيطة ، أولا التطبيق المستدعى يجب أن يحتوي على تصريح عن الدالة أو الإجراء ،بعد ذلك يمكنك استدعاء الدالة أو الإجراء كما تفعل عادةً فى Object Pascal .

لاستيراد دالة أو إجراء محتوى فى DLL استخدم الكلمة المفتاحية External فى تصريح الدالة أو الإجراء ،كمثال لنأخذ الإجراء SayHell الذى رأيناه سابقاً ،التصريح عنه فى التطبيق المستدعى يكون كالتالي:

procedure SayHello(AForm : TForm); external `testdll.dll';
الكلمة المحجوزة External تخبر المترجم أن الإجراء يمكن أن يكون فى DLL (فى حالتنا هذه testdll.dll) ، 
الاستدعاء الفعلي للإجراء( وهو الخطوة التالية وحيثما احتجت إليه) لا يظهر أي اختلاف عن أي إجراء آخر.
SayHello(Self);
بالطبع فإن جملة التصريح عن الإجراء أو الدالة يجب أن تكون مطابقة للشكل الذي ظهر به فى DLL ، الجدير بالقول هنا أن هذه 
هى الحالة الوحيدة التى تتأثر فيها Object Pascal بحالة الحروف كبيرة أم صغيرة ،فإن أي خطأ تقع فيه في تهجئة أو شكل الحروف
 فى إسم الدالة أو الإجراء أثناء تصريحك عنه سيؤدي إلى اعتراض فى RunTime وتطبيقك سيرفض التحميل.
استخدام الكلمة المحجوزة External :
هذه الكلمة تملك ثلاث حالات لاستيراد إجراء أو دالة من DLL :
1-باستخدام الإسم الفعلي :في هذه الطريقة يمكنك أن تصرح عن إسم الدالة أو الإجراء بالضبط كما جاء في DLL مثلاً :
procedure SayHello(AForm : TForm); external `testdll.dll';
 
2-باستخدام القيمة الترتيبية :فى هذه الطريقة عليك أن تعين قيمة لترتيب الدالة أو الإجراء كما صدّر من DLL مثلاً:
procedure SomeOrdinalProcedure;  external `testdll.dll' index 99;
 
فى هذا المثال قمت باستيراد الإجراء الذي صدر من DLL عندما index 99 ،أستطيع تسمية الإجراء أي إسم أريده فى التطبيق المستدعي ،
وسيكون المقصود هو ذلك الإجراء فى DLL الذي له نفس قيمة الترتيب المذكور بعد index ، أستطيع تسمية الإجراء أي إسم أريده لأن
 الإجراء مصدّر بواسطة قيمة الترتيب وليس بواسطة اسمه .
 
     3-باستخدام إعادة التسمية :تسمح لي هذه الطريقة بأن أستورد الإجراء بواسطة اسمه الأصلي ،لكن أعطي الإجراء اسما جديداً فى التطبيق 
المستدعي ، وهذا مشابه للتالي :
 
procedure CoolProcedure; 
  external `testdll.dll' name `DoSomethingReallyCool';
 
هنا استوردت الإجراء المدعو DoSomethingReallyCool وأعدت تسميته باسم CoolProcedure .
 
ملاحظة/
تعد الطريقة الأولى أي الإستيراد بواسطة الإسم هي المستخدمة عادةً .
 
كما رأينا فإن التحميل الأستاتيكي يوفر لك كل ما تريده  في التعامل مع DLL ، إلا إذا احتجت إلى المرونة التي يوفرها التحميل الديناميكي .
 
ثانياً استدعاء الدوال والإجراءات باستخدام التحميل الديناميكي :
 
إن استدعاء الدوال والإجراءات المحملة ديناميكياً يتطلب منك أن تعرف مؤشر للإجراء أو الدالة الموجودة فى DLL (سبق وقدمت شرحا لموضوع 
مؤشر إجراء أو دالة في الدرس السابق) ،لشرح هذا الموضوع لنفترض أنك تملك إجراء في DLL يدعى SayHello وهو يمكن أن يظهر كالتالي :
procedure SayHello(AForm : TForm);
begin
  MessageBox(AForm.Handle, `Hello From a DLL!',
    `DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
لاستدعاء هذا الإجراء من برنامجك ،فإنك يجب أن تصرح عن نوع يصف هذا الإجراء كالتالي :
type
  TSayHello = procedure(AForm : TForm);
بعد أن تفعل ذلك ،عليك أن تحمل DLL واستخدام الدالة أو الإجراء ثم إلغاء تحميله عند انتهاء العمل معه (استخدم الدوال المذكورة 
فى بداية هذا  الدرس لتحقيق ذلك)،
 وهذا هو مثالنا كاملاً :
var
  DLLInstance : THandle;
  SayHello : TSayHello;
begin
  { Load the DLL. }
  DLLInstance := LoadLibrary(`testdll.dll');
  { Get the address of the procedure. }
  @SayHello := GetProcAddress(DLLInstance, `SayHello');
  { Call the procedure. }
  SayHello(Self);
  { Unload the DLL. }
  FreeLibrary(DLLInstance);
end;
من الأفضل أن تستخدم code إضافي لفحص الخطأ ، ففي بعض الأحيان تفشل الدالة LoadLibrary في التقاط ممسك ملف DLL ،أو تفشل 
الدالة GetprocAddress فى التقاط عنوان الإجراء أو الدالة ، فتلافيا لتوقف البرنامج عند حدوث ذلك نضيف هذا الفحص :
procedure TForm1.DynamicLoadBtnClick(Sender: TObject);
type
  TSayHello = procedure(AForm : TForm);
var
  DLLInstance : THandle;
  SayHello : TSayHello;
begin
  DLLInstance := LoadLibrary(`testdll.dll');
  if DLLInstance = 0 then begin
    MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);
    Exit;
  end;
  @SayHello := GetProcAddress(DLLInstance, `SayHello');
  if @SayHello <> nil then
    SayHello(Self)
  else
    MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);
  FreeLibrary(DLLInstance);
end;
والآن... بناء تطبيق DLL باستخدام مخزن الأغراض :
لبناء DLL في دلفي اتبع الخطوات التالية :
1-اختار File ثم New لعرض Object Repository ،كما بالشكل التالي:
 

2-أنقر نقرتين مزدوجتين على أيقونة DLL ، عند ذلك فإن دلفي تبني تطبيق DLL جديد وتعرض محرر الكود كما بالشكل التالي :

 

 

الآن يمكنك أن تبدأ بإضافة ال code إلى DLL ،تأكد من إضافة إسم أي دالة أو إجراء مصدرة في جزء Exports ،بعد أن تنتهي من إضافة الكود بإمكانك اختيار Compile أو Built من قائمة Project لبناء DLL .

 التعليق الظاهر في وحدة DLL :

بالتأكيد لاحظت التعليق الطويل الذي يظهر فى بداية وحدة DLL ، إن هذا التعليق يخبرك أنه إذا احتوى DLL على دوال أو إجراءات مصدرة تأخذ نوع سلسلة طويلة كمحدد لها ، أو إذا كانت الدالة ترجع نوع سلسلة طويلة فعليك فعل التالي :

1-ضع ShareMem في بداية قائمة Uses في كل من DLL والوحدة الرئيسية للتطبيق المستدعي ،تأكد من أن ShareMem تأتي قبل الوحدات الأخرى فى قائمة Uses .

2-إشحذ ملف Borlandmm.dll مع DLL ،لاحظ أني قلت Borlandmm.dll وليس Delphimm.dll كما أشار التعليق الموجود في DLL ،ذلك أن Borland غيرت إسم مدير ذاكرة DLL لكنها أهملت تغيير التعليقات المنتجة عندما تبني DLL جديد ،ومع ذلك فإن التعليق ليس مضللا بالكامل ، لأن كلا من Delphimm.dll و Borlandmm.dll مضمنان في Delphi4 .

لتجنب هذه الطلبات ، فقط تأكد من أن دوالك وإجراءاتك في DLL لا تأخذ محددات نوع سلسلة طويلة ودوال DLL لا ترجع سلسلة طويلة ، بدلا عن استخدام سلسلة طويلة (Long String) بإمكانك استخدام Pchar String أو Short String مثلا : عوضا عن استخدام :

procedure MyProcedure(var S : string);
begin
  { Procedure code here. }
end;
إستخدم:  
 
procedure MyFunction(S : PChar);
begin
{ Procedure code here. }
end;

 هذا الأمر سهل الإستخدام لذلك أنت لا تحتاج مطلقا إلى Borlandmm.dll ، أنت فقط بحاجة لأن تعي القيود الموضوعة على استخدام السلاسل الطويلة في دوال وإجراءات DLL ، لاحظ أنك تستطيع استخدام السلاسل الطويلة في الدوال والإجراءات المستخدمة داخلياً في DLL ،إذاً القيود السابقة تنطبق فقط على الدوال والإجراءات المصدرة.

ملاحظة :

بعد أن فهمت ما يعنيه التعليق بإمكانك مسحه ، لأنه لم يعد ضرورياً بالنسبة لك.

تطبيق عملي:

إليك الآن هذه الأكواد الثلاثة التي تعطي مثالاً للمفاهيم المناقشة سابقاً ،الأول يحتوي DLL سيتم استدعاؤه ستاتيكياً من التطبيق المستدعي ،الثاني يحتوي DLL سيستدعى ديناميكياً من الإجراء المستدعي بأدوات DLLProc ،أما الثالث فهو يحتوي البرنامج الذي يستدعي DLL السابقين ،البرنامج يملك form مع أربع buttons  التى تستدعي إجراءات متعددة موجودة في ملفيDLLs .

TestDll.dpr.

library TestDLL;
uses
  SysUtils,
  Classes,
  Forms,
  Windows;
procedure SayHello(AForm : TForm);
begin
  MessageBox(AForm.Handle, `Hello From a DLL!',
    `DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
procedure DoSomething;
begin
  MessageBox(0, `This procedure was exported by ordinal.',
    `DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
procedure DoSomethingReallyCool;
begin
  MessageBox(0, `Something really cool.',
    `DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
exports
  SayHello,
  DoSomething index 99,
  DoSomethingReallyCool;
begin
end.

DynLoad.dpr.

library TestDLL;
uses
  SysUtils,
  Classes,
  Forms,
  Windows;
procedure MyDLLProc(Reason: Integer);
begin
  if Reason = DLL_PROCESS_DETACH then
    { DLL styunloading. Cleanup code here. }
  MessageBox(0, `DLL styunloading!',
    `DLL Message', MB_OK or MB_ICONEXCLAMATION);
end;
procedure SayHelloDyn(AForm : TForm);
begin
  MessageBox(AForm.Handle, `Hello From a DLL!' + #13 +
    `This DLL was loaded dynamically',
    `DLL Message', MB_OK or MB_ICONEXCLAMATION); 
end;
exports
  SayHelloDyn;
begin
  DLLProc := @MyDLLProc;
end.

CallDllU.pas.

unit CallDLLU;
2">`Dface
uses
  Windows, Messages, SysUtils, Classes, G"ltr">
  Controls, Forms, Dialogs, StdCtrls;
type
  TForm1 = class(TForm)
    HelloBtn: TButton;
    OrdBtn: TButton;
    DynamicLoadBtn: TButton;
     `HedBtn: TButton;
    procedure HelloBtnClick(Sender: TObject);
    procedure OrdBtnClick(Sender: TObject);
    procedure DynamicLoadBtnClick(Sender: TObject);
    procedure  `HedBtnClick(Sender: TObject);
  private
    { Private declarMessag }
  public
    { Public declarMessag }
  end;
var
  Form1: TForm1;
{ A procedure imported by name. }
procedure SayHello(AForm : TForm);
  external `testdll.dll';
{ A procedure imported by ordinal. }
procedure OrdinalProcedure;
  external `testdll.dll' index 99;
{ A procedure imported and renamed. }
procedure CoolProcedure;
  external `testdll.dll' name `DoSomethingReallyCool';
2mpfont-fMessa
{$R *.DFM}
procedure TForm1.HelloBtnClick(Sender: TObject);
begin
  SayHello(Self);
end;
procedure TForm1.OrdBtnClick(Sender: TObject); 
begin
  OrdinalProcedure;
end;
procedure TForm1. `HedBtnClick(Sender: TObject);
begin
  CoolProcedure;
end;
procedure TForm1.DynamicLoadBtnClick(Sender: TObject);
type
  TSayHello = procedure(AForm : TForm);
var
  DLLInstance : THandle;
  SayHello    : TSayHello;
begin
  { Load the DLL. }
  DLLInstance := LoadLibrary(`DynLoad.dll');
  { If loading fails then bail out. }
  if DLLInstance = 0 then begin
    MessageDlg(`Unable to load DLL.', mtError, [mbOK], 0);
    Exit;
  end;
  { Assign the procedure po2">`D. }
  @SayHello := GetProcAddress(DLLInstance, `SayHelloDyn');
  { If the procedure was found then call it. }
  if @SayHello <> nil then
    SayHello(Self)
  else
    MessageDlg(`Unable to locate procedure.', mtError, [mbOK], 0);
  { Unload the DLL. }
  FreeLibrary(DLLInstance);
end;
end. 

 

 

&nbsكيف نحمل وحدة DLL ثمsكيف نستدعي دالة أو إجراء من DLL واتجهنا إلى التطبيق العملي لإيضاح بناء و>  external `testdll.dll' name `DoSomethingReallyCool';
إلى هنا نكونt>&nنا لل DLL حيث أننا أصبحنt>ادرين على إنشائه و>>

 ....وp; موفق .... (خ.غ)

  &nbp-size: 11ptnt-family: 'Times New Roman';">  
<<<"2">    08">الدرس السابقn>
  ><>المنتدى>   >الصفحة الرئيسية
>><1pt; font-family: 'Times New Roman';"><,DynamicLoadBtn:s, Dialogddddddtn: TButton;
  DoSo		';"><33%>><>اختر التe="2">Wمناسب:<1tdsty		';d'Times Nmes Newman';"><33%>>ty		'font color="#0000ff">ضعيف ty		'nt siz; font-fLL;
_; fon>
1ty		'nt siz; font-2LL;
_; fon>
2ty		'nt siz; font-3LL;
_; fon>
3ty		'nt siz; font-4LL;
_; fon>
4ty		'nt sizchecked="checked"z; font-5LL;
_; fon>
5ty		'font color="#0000ff">  ممتازty		'1tdsty		';d'Times Nrightwman';"><34%WindLL;submifamily: 'submifa><1tdsty		'/n sty		''Times New Romayle="";">DynamicLoadB: TBut-s New Re: s New Re;tn: TButton;<: TButcolor="#111111;
<3ont size="2">usesSayHmes New Rnt-size:b><> تعليقات سابقة:  	 11pms, Dialogs, StdCtrls;  Classes'830'sp; usesClasses'830'sp;L;#1essageBoxFolorm.HusesSayHm New Roman';">  <152t3le__><هاوي__

95le="p "ltrfami<192, 192, 192sp;&السلام عليكم:p>Winوس أحلى وأحلا، وبدأت">Wغشاوة تتبدد، لكن لا أظن أن دy: 'Tidll/oينبغي التوقفew Rها، بل نريد منك أختي نجاة المزيد من الأمثلة التطبيقية،/oلأنها أفضل سبيل للإلمام وp;فهم:p>els:'أضنe="2">ستحسن إعادة تسمية هذه المصطلحات (إن لم">كن شاقا) للتلاءم مع الترجمات الورقية لكتب الد;في::p>pan(legul.sps) محددات ==> بارامترات:p>  >ty ';">pNew Roman#1es &nbsel">p[رابط التعليق]<1tdsty ';d'Times New Roman';">pخياze="2"مشرفين:ty /pr[New Roman';">&nbsnt>حذف&nbsnt>تعدily: ']ty           ty <1tdsty <1trsty <1times New Romayle=""   Classes'933'sp; usesClasses'933'sp;L;#2essageBoxFolorm.HusesSayHm New Roman';"> 

95le="p "ltrfami<192, 192, 192sp;&الحمد ;&nbقمت بإضافة indىt>مفيداً."p "ltrn style="">  >ty ';">pNew Roman#2es &nbsel">p[رابط التعليق]<1tdsty ';d'Times New Roman';">pخياze="2"مشرفين:ty /pr[New Roman';">&nbsnt>حذف&nbsnt>تعدily: ']ty           ty <1tdsty <1trsty <1times New Romayle=""   &nbsVTimdapa(/span> ="2"-size: 11newCnal.sp."ltr".; fonllo F"ize: 11pt fan>&("عفواً، لم"تكتب عنوان للتعليق!"i;ty -size: 11newCnal.sp."ltr".fsizs();11ptstyle="font-sty }11pt; f ="2"-size: 11newCnal.sp.tyle.; fonllo F"ize: 11pt fan>&("عفواً، لم"تكتب نص للتعليق!"i;ty -size: 11newCnal.sp.tyle.fsizs();11ptstyle="font-sty="font t; f orms,t size="2"brsty rn style="">9">typetdsty "ize=";newCnal.sp""2">  &nbfo/ssubmif="orms,9">ty style="font- rnr">ao/sclick">p; usesSayHm   ty :b><>ty أضف تعليقty 9">ty style="fone>

9"> 9"> t-sizeeeeeeeeنص التعليق:tyleHmes extarza><1ptt-sizeeeeeeeely: 'Timesmes New pan style="foina"z; font-اضف التعليقamily: 'submifa>    ty <1ptt-sizeeeeeeeelfamnt-zeeeeeeeelle="";"n';">DoSo r"n';">D "font-smEND here-sizeifa>

  Messag font-family: 'Timesman';">SayHelloDyn;<2y: 'Times N69"tr">SayHelloDyn;<22: 'Times 509"tr">
    Messag font-family: 'Timesme dir="ltr">
re dily: 'Times New <2">Form size="2">  p; >الرئيسية<>::="ltr"><2">Form size="2">  e: 11pt; font-fami>Winوس<>::="ltr"><2">Form size="2">  >ال <>::="ltr"><2">Form size="2">  2"> 
ze="2">Messa "font> <>::="ltr"><2">Form size="2">  an';"> &nbs "font> <>::="ltr"><2">Form /span>
<>::="ltr"><2">Form -size: 11pt; font-family: 'Times New Ro>
ethiتكررة&nbs  "font><>::="ltr"><2">Form -size: 11pt; font-family: 'Time size="2">MeHandle, `Hep>size="2">  >الy: 'Timbs  "font><>::="ltr"><2">Form /o>size="2">  >ال">begin<>::="ltr"><2">Form /o>size="2">  >re>
<>::="ltr"><2">Form /o>size="2">  mejhar="2">>rfont size="2">bs  "font><>::="ltr"><2">Form /o>size="2">  `Dview="2">>="2ء مع مسؤول تقنيn<>::="ltr"><2">Form /o>size="2">  >">DynLoad.dpr.<>::="ltr"><2">Form /o>size="2">  >11pt; font-a><1pt; font-family: w Roman'sme dir="ltsme dir="ltrt>
re dis="ltr">SayHelloDyn;<2Ctrls;<1pt; font-family: w Roman'rt>
an';">pC<1 dir="ltsme dir="ltrt>
<2">Form 'Times New Roman';">begin <>::="ltr"><2">Form -size: 11mailto:webmayle=tr">bs "font><>::="ltr"><2">Form /o>size="2">  ="2">beginمن نحن">bs "font><>::="ltr"><2">Form /o>size="2">  =is DLL wأعلن لديناont-fam/a "font><1tdstyfamily: w Roman'sme dir="ltsme dir="ltrt>
re dis="ltr">SayHelloDyn;<2Ctrls;<1pt; font-family: w Roman'rt>
an';">pC<1 dir="ltsme dir="ltrt><2">Form font ><>::="ltr"><2">Form <2">Form tyle=nt>تصميم : فريقethingReallyCool;< font><>::="ltr"><2">Form font ><>::="ltr"><2">Form <2">Form البرنامج المستخدم : thingReal3.0ir="ltr"><>::="ltr">ir="ltr"><1tdstyfamily: style="font-sifamily: 'Times Neont-family: 'Times New Ro/o/o/orp>